﻿using EasyNet.Componets.Core.Caching;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EasyNet.Componets.Core;
using EasyNet.Componets.Cache.Redis.StackExchange;

namespace EasyNet.Componets.Cache.Redis
{
 
    public class StackExchangeRedis : ICache
    {
        private readonly IDatabase _cache;
        private readonly ConnectionMultiplexer _connectionMultiplexer;
        private readonly Encoding _encoding = Encoding.UTF8;

        /// <summary>
        /// 缓存时，是否把具体类型缓存，用于保证继承关系存储和重建
        /// </summary>
        public bool IsCahceObjectType { get; set; }

        public StackExchangeRedis(string connectionStringName, bool isCahceObjectType)
        {
            var connection = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
            _connectionMultiplexer = ConnectionMultiplexer.Connect(connection);
            _cache = _connectionMultiplexer.GetDatabase();

            IsCahceObjectType = isCahceObjectType;
        }

        public StackExchangeRedis(string connectionStringName)
            : this(connectionStringName, false)
        {
        }

        public T Get<T>(string key)
        {
            string value = _cache.StringGet(key);
            return ConstructObject<T>(key, value);
        }

        public T[] Get<T>(string[] keys)
        {
            var rediskeys = keys.Select(key => (RedisKey)key).ToArray();
            var redisValues = _cache.StringGet(rediskeys);

            IList<T> results = new List<T>();

            for (int i = 0; i < redisValues.Count(); i++)
            {
                var redisValue = redisValues[i];
                var redisKey = rediskeys[i];

                if (redisValue.HasValue)
                {
                    results.Add(ConstructObject<T>(redisKey, redisValue));
                }
            }

            return results.ToArray();
        }

        public void Set(string key, object data, int cacheTimeInMinute)
        {
            TimeSpan span = DateTime.UtcNow.AddMinutes(cacheTimeInMinute) - DateTime.UtcNow;
            var obj = ConstructRedisValue(data);

            _cache.StringSet(key, obj, span);
        }

        public void Set(string key, object data)
        {
            var obj = ConstructRedisValue(data);

            _cache.StringSet(key, obj);
        }

        public void Set(Dictionary<string, object> keyValues)
        {
            if (keyValues.Any())
            {
                var batch = _cache.CreateBatch();
                var addTasks = new List<Task>();

                foreach (var keyValuePair in keyValues)
                {
                    var obj = ConstructRedisValue(keyValuePair.Value);
                    var addAsync = batch.StringSetAsync(keyValuePair.Key, obj);
                    addTasks.Add(addAsync);
                }
                batch.Execute();
                var tasks = addTasks.ToArray();
                Task.WaitAll(tasks);
            }
        }

        /// <summary>
        /// 根据缓存数据构造反序列化的目标对象
        /// </summary>
        private T ConstructObject<T>(string cacheKey, string cacheValue)
        {
            T obj = default(T);

            if (!string.IsNullOrEmpty(cacheValue))
            {
                if (IsCahceObjectType)
                {
                    try
                    {
                        var cahceItem = cacheValue.DeserializeFromJson<CacheItem>();

                        //可能由于旧数据缓存而导致反序列化不一样
                        if (string.IsNullOrWhiteSpace(cahceItem.CachedObjectJsonString))
                        {
                            Remove(cacheKey);
                        }
                        else
                        {
                            obj = (T)cahceItem.GetCachedObject();
                        }
                    }
                    catch (System.Runtime.Serialization.SerializationException)
                    {
                        //可能由于旧数据缓存而导致反序列化失败，故而移除缓存
                        Remove(cacheKey);
                    }
                }
                else
                {
                    obj = cacheValue.DeserializeFromJson<T>();
                }
            }

            return obj;
        }

        /// <summary>
        /// 根据需要缓存的对象构造RedisValue
        /// </summary>
        private RedisValue ConstructRedisValue(object data)
        {
            RedisValue obj;

            if (IsCahceObjectType)
            {
                var cacheItem = new CacheItem(data);
                obj = cacheItem.ToJsonString();
            }
            else
            {
                obj = data.ToJsonString();
            }

            return obj;
        }

        public bool IsSet(string key)
        {
            return _cache.KeyExists(key);
        }

        public void Remove(string key)
        {
            _cache.KeyDelete(key);
        }

        public void Clear()
        {
            var endpoints = _connectionMultiplexer.GetEndPoints(true);
            foreach (var endpoint in endpoints)
            {
                var server = _connectionMultiplexer.GetServer(endpoint);
                server.FlushAllDatabases();
            }
        }
    }
}
